/*
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.mybatis.generator.config.xml;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;

import org.mybatis.generator.config.ColumnOverride;
import org.mybatis.generator.config.ColumnRenamingRule;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.GeneratedKey;
import org.mybatis.generator.config.IgnoredColumn;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.JavaTypeResolverConfiguration;
import org.mybatis.generator.config.ModelType;
import org.mybatis.generator.config.PluginConfiguration;
import org.mybatis.generator.config.PropertyHolder;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;
import org.mybatis.generator.exception.XMLParserException;
import org.mybatis.generator.internal.util.StringUtility;
import org.mybatis.generator.internal.util.messages.Messages;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * This class parses configuration files into the new Configuration API
 * 
 * @author Jeff Butler
 */
public class MyBatisGeneratorConfigurationParser {
    private Properties properties;

    public MyBatisGeneratorConfigurationParser(Properties properties) {
	super();
	if (properties == null) {
	    this.properties = System.getProperties();
	}
	else {
	    this.properties = properties;
	}
    }

    public Configuration parseConfiguration(Element rootNode)
	    throws XMLParserException {

	Configuration configuration = new Configuration();

	NodeList nodeList = rootNode.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("properties".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperties(configuration, childNode);
	    }
	    else if ("classPathEntry".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseClassPathEntry(configuration, childNode);
	    }
	    else if ("context".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseContext(configuration, childNode);
	    }
	}

	return configuration;
    }

    private void parseProperties(Configuration configuration, Node node)
	    throws XMLParserException {
	Properties attributes = parseAttributes(node);
	String resource = attributes.getProperty("resource"); //$NON-NLS-1$
	String url = attributes.getProperty("url"); //$NON-NLS-1$

	if (!StringUtility.stringHasValue(resource)
	        && !StringUtility.stringHasValue(url)) {
	    throw new XMLParserException(Messages.getString("RuntimeError.14")); //$NON-NLS-1$
	}

	if (StringUtility.stringHasValue(resource)
	        && StringUtility.stringHasValue(url)) {
	    throw new XMLParserException(Messages.getString("RuntimeError.14")); //$NON-NLS-1$
	}

	URL resourceUrl;

	try {
	    if (StringUtility.stringHasValue(resource)) {
		resourceUrl = Thread.currentThread().getContextClassLoader()
		        .getResource(resource);
		if (resourceUrl == null) {
		    throw new XMLParserException(Messages.getString("RuntimeError.15", resource)); //$NON-NLS-1$
		}
	    }
	    else {
		resourceUrl = new URL(url);
	    }

	    InputStream inputStream = resourceUrl.openConnection()
		    .getInputStream();

	    properties.load(inputStream);
	    inputStream.close();
	} catch (IOException e) {
	    if (StringUtility.stringHasValue(resource)) {
		throw new XMLParserException(Messages.getString("RuntimeError.16", resource)); //$NON-NLS-1$
	    }
	    else {
		throw new XMLParserException(Messages.getString("RuntimeError.17", url)); //$NON-NLS-1$
	    }
	}
    }

    private void parseContext(Configuration configuration, Node node) {

	Properties attributes = parseAttributes(node);
	String defaultModelType = attributes.getProperty("defaultModelType"); //$NON-NLS-1$
	String targetRuntime = attributes.getProperty("targetRuntime"); //$NON-NLS-1$
	String introspectedColumnImpl = attributes
	        .getProperty("introspectedColumnImpl"); //$NON-NLS-1$
	String id = attributes.getProperty("id"); //$NON-NLS-1$

	ModelType mt = defaultModelType == null ? null : ModelType
	        .getModelType(defaultModelType);

	Context context = new Context(mt);
	context.setId(id);
	if (StringUtility.stringHasValue(introspectedColumnImpl)) {
	    context.setIntrospectedColumnImpl(introspectedColumnImpl);
	}
	if (StringUtility.stringHasValue(targetRuntime)) {
	    context.setTargetRuntime(targetRuntime);
	}

	configuration.addContext(context);

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(context, childNode);
	    }
	    else if ("plugin".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parsePlugin(context, childNode);
	    }
	    else if ("commentGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseCommentGenerator(context, childNode);
	    }
	    else if ("jdbcConnection".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseJdbcConnection(context, childNode);
	    }
	    else if ("javaModelGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseJavaModelGenerator(context, childNode);
	    }
	    else if ("javaTypeResolver".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseJavaTypeResolver(context, childNode);
	    }
	    else if ("sqlMapGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseSqlMapGenerator(context, childNode);
	    }
	    else if ("javaClientGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseJavaClientGenerator(context, childNode);
	    }
	    else if ("table".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseTable(context, childNode);
	    }
	}
    }

    private void parseSqlMapGenerator(Context context, Node node) {
	SqlMapGeneratorConfiguration sqlMapGeneratorConfiguration = new SqlMapGeneratorConfiguration();

	context.setSqlMapGeneratorConfiguration(sqlMapGeneratorConfiguration);

	Properties attributes = parseAttributes(node);
	String targetPackage = attributes.getProperty("targetPackage"); //$NON-NLS-1$
	String targetProject = attributes.getProperty("targetProject"); //$NON-NLS-1$

	sqlMapGeneratorConfiguration.setTargetPackage(targetPackage);
	sqlMapGeneratorConfiguration.setTargetProject(targetProject);

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(sqlMapGeneratorConfiguration, childNode);
	    }
	}
    }

    private void parseTable(Context context, Node node) {
	TableConfiguration tc = new TableConfiguration(context);
	context.addTableConfiguration(tc);

	Properties attributes = parseAttributes(node);
	String catalog = attributes.getProperty("catalog"); //$NON-NLS-1$
	String schema = attributes.getProperty("schema"); //$NON-NLS-1$
	String tableName = attributes.getProperty("tableName"); //$NON-NLS-1$
	String domainObjectName = attributes.getProperty("domainObjectName"); //$NON-NLS-1$
	String alias = attributes.getProperty("alias"); //$NON-NLS-1$
	String enableInsert = attributes.getProperty("enableInsert"); //$NON-NLS-1$
	String enableSelectByPrimaryKey = attributes
	        .getProperty("enableSelectByPrimaryKey"); //$NON-NLS-1$
	String enableSelectByExample = attributes
	        .getProperty("enableSelectByExample"); //$NON-NLS-1$
	String enableUpdateByPrimaryKey = attributes
	        .getProperty("enableUpdateByPrimaryKey"); //$NON-NLS-1$
	String enableDeleteByPrimaryKey = attributes
	        .getProperty("enableDeleteByPrimaryKey"); //$NON-NLS-1$
	String enableDeleteByExample = attributes
	        .getProperty("enableDeleteByExample"); //$NON-NLS-1$
	String enableCountByExample = attributes
	        .getProperty("enableCountByExample"); //$NON-NLS-1$
	String enableUpdateByExample = attributes
	        .getProperty("enableUpdateByExample"); //$NON-NLS-1$
	String selectByPrimaryKeyQueryId = attributes
	        .getProperty("selectByPrimaryKeyQueryId"); //$NON-NLS-1$
	String selectByExampleQueryId = attributes
	        .getProperty("selectByExampleQueryId"); //$NON-NLS-1$
	String modelType = attributes.getProperty("modelType"); //$NON-NLS-1$
	String escapeWildcards = attributes.getProperty("escapeWildcards"); //$NON-NLS-1$
	String delimitIdentifiers = attributes
	        .getProperty("delimitIdentifiers"); //$NON-NLS-1$
	String delimitAllColumns = attributes.getProperty("delimitAllColumns"); //$NON-NLS-1$

	if (StringUtility.stringHasValue(catalog)) {
	    tc.setCatalog(catalog);
	}

	if (StringUtility.stringHasValue(schema)) {
	    tc.setSchema(schema);
	}

	if (StringUtility.stringHasValue(tableName)) {
	    tc.setTableName(tableName);
	}

	if (StringUtility.stringHasValue(domainObjectName)) {
	    tc.setDomainObjectName(domainObjectName);
	}

	if (StringUtility.stringHasValue(alias)) {
	    tc.setAlias(alias);
	}

	if (StringUtility.stringHasValue(enableInsert)) {
	    tc.setInsertStatementEnabled(StringUtility.isTrue(enableInsert));
	}

	if (StringUtility.stringHasValue(enableSelectByPrimaryKey)) {
	    tc.setSelectByPrimaryKeyStatementEnabled(StringUtility
		    .isTrue(enableSelectByPrimaryKey));
	}

	if (StringUtility.stringHasValue(enableSelectByExample)) {
	    tc.setSelectByExampleStatementEnabled(StringUtility
		    .isTrue(enableSelectByExample));
	}

	if (StringUtility.stringHasValue(enableUpdateByPrimaryKey)) {
	    tc.setUpdateByPrimaryKeyStatementEnabled(StringUtility
		    .isTrue(enableUpdateByPrimaryKey));
	}

	if (StringUtility.stringHasValue(enableDeleteByPrimaryKey)) {
	    tc.setDeleteByPrimaryKeyStatementEnabled(StringUtility
		    .isTrue(enableDeleteByPrimaryKey));
	}

	if (StringUtility.stringHasValue(enableDeleteByExample)) {
	    tc.setDeleteByExampleStatementEnabled(StringUtility
		    .isTrue(enableDeleteByExample));
	}

	if (StringUtility.stringHasValue(enableCountByExample)) {
	    tc.setCountByExampleStatementEnabled(StringUtility
		    .isTrue(enableCountByExample));
	}

	if (StringUtility.stringHasValue(enableUpdateByExample)) {
	    tc.setUpdateByExampleStatementEnabled(StringUtility
		    .isTrue(enableUpdateByExample));
	}

	if (StringUtility.stringHasValue(selectByPrimaryKeyQueryId)) {
	    tc.setSelectByPrimaryKeyQueryId(selectByPrimaryKeyQueryId);
	}

	if (StringUtility.stringHasValue(selectByExampleQueryId)) {
	    tc.setSelectByExampleQueryId(selectByExampleQueryId);
	}

	if (StringUtility.stringHasValue(modelType)) {
	    tc.setConfiguredModelType(modelType);
	}

	if (StringUtility.stringHasValue(escapeWildcards)) {
	    tc.setWildcardEscapingEnabled(StringUtility.isTrue(escapeWildcards));
	}

	if (StringUtility.stringHasValue(delimitIdentifiers)) {
	    tc.setDelimitIdentifiers(StringUtility.isTrue(delimitIdentifiers));
	}

	if (StringUtility.stringHasValue(delimitAllColumns)) {
	    tc.setAllColumnDelimitingEnabled(StringUtility
		    .isTrue(delimitAllColumns));
	}

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(tc, childNode);
	    }
	    else if ("columnOverride".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseColumnOverride(tc, childNode);
	    }
	    else if ("ignoreColumn".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseIgnoreColumn(tc, childNode);
	    }
	    else if ("generatedKey".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseGeneratedKey(tc, childNode);
	    }
	    else if ("columnRenamingRule".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseColumnRenamingRule(tc, childNode);
	    }
	}
    }

    private void parseColumnOverride(TableConfiguration tc, Node node) {
	Properties attributes = parseAttributes(node);
	String column = attributes.getProperty("column"); //$NON-NLS-1$
	String property = attributes.getProperty("property"); //$NON-NLS-1$
	String javaType = attributes.getProperty("javaType"); //$NON-NLS-1$
	String jdbcType = attributes.getProperty("jdbcType"); //$NON-NLS-1$
	String typeHandler = attributes.getProperty("typeHandler"); //$NON-NLS-1$
	String delimitedColumnName = attributes
	        .getProperty("delimitedColumnName"); //$NON-NLS-1$

	ColumnOverride co = new ColumnOverride(column);

	if (StringUtility.stringHasValue(property)) {
	    co.setJavaProperty(property);
	}

	if (StringUtility.stringHasValue(javaType)) {
	    co.setJavaType(javaType);
	}

	if (StringUtility.stringHasValue(jdbcType)) {
	    co.setJdbcType(jdbcType);
	}

	if (StringUtility.stringHasValue(typeHandler)) {
	    co.setTypeHandler(typeHandler);
	}

	if (StringUtility.stringHasValue(delimitedColumnName)) {
	    co.setColumnNameDelimited(StringUtility.isTrue(delimitedColumnName));
	}

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(co, childNode);
	    }
	}

	tc.addColumnOverride(co);
    }

    private void parseGeneratedKey(TableConfiguration tc, Node node) {
	Properties attributes = parseAttributes(node);

	String column = attributes.getProperty("column"); //$NON-NLS-1$
	boolean identity = StringUtility.isTrue(attributes
	        .getProperty("identity")); //$NON-NLS-1$
	String sqlStatement = attributes.getProperty("sqlStatement"); //$NON-NLS-1$
	String type = attributes.getProperty("type"); //$NON-NLS-1$

	GeneratedKey gk = new GeneratedKey(column, sqlStatement, identity, type);

	tc.setGeneratedKey(gk);
    }

    private void parseIgnoreColumn(TableConfiguration tc, Node node) {
	Properties attributes = parseAttributes(node);
	String column = attributes.getProperty("column"); //$NON-NLS-1$
	String delimitedColumnName = attributes
	        .getProperty("delimitedColumnName"); //$NON-NLS-1$

	IgnoredColumn ic = new IgnoredColumn(column);

	if (StringUtility.stringHasValue(delimitedColumnName)) {
	    ic.setColumnNameDelimited(StringUtility.isTrue(delimitedColumnName));
	}

	tc.addIgnoredColumn(ic);
    }

    private void parseColumnRenamingRule(TableConfiguration tc, Node node) {
	Properties attributes = parseAttributes(node);
	String searchString = attributes.getProperty("searchString"); //$NON-NLS-1$
	String replaceString = attributes.getProperty("replaceString"); //$NON-NLS-1$

	ColumnRenamingRule crr = new ColumnRenamingRule();

	crr.setSearchString(searchString);

	if (StringUtility.stringHasValue(replaceString)) {
	    crr.setReplaceString(replaceString);
	}

	tc.setColumnRenamingRule(crr);
    }

    private void parseJavaTypeResolver(Context context, Node node) {
	JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();

	context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);

	Properties attributes = parseAttributes(node);
	String type = attributes.getProperty("type"); //$NON-NLS-1$

	if (StringUtility.stringHasValue(type)) {
	    javaTypeResolverConfiguration.setConfigurationType(type);
	}

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(javaTypeResolverConfiguration, childNode);
	    }
	}
    }

    private void parsePlugin(Context context, Node node) {
	PluginConfiguration pluginConfiguration = new PluginConfiguration();

	context.addPluginConfiguration(pluginConfiguration);

	Properties attributes = parseAttributes(node);
	String type = attributes.getProperty("type"); //$NON-NLS-1$

	pluginConfiguration.setConfigurationType(type);

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(pluginConfiguration, childNode);
	    }
	}
    }

    private void parseJavaModelGenerator(Context context, Node node) {
	JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration();

	context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);

	Properties attributes = parseAttributes(node);
	String targetPackage = attributes.getProperty("targetPackage"); //$NON-NLS-1$
	String targetProject = attributes.getProperty("targetProject"); //$NON-NLS-1$

	javaModelGeneratorConfiguration.setTargetPackage(targetPackage);
	javaModelGeneratorConfiguration.setTargetProject(targetProject);

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(javaModelGeneratorConfiguration, childNode);
	    }
	}
    }

    private void parseJavaClientGenerator(Context context, Node node) {
	JavaClientGeneratorConfiguration javaClientGeneratorConfiguration = new JavaClientGeneratorConfiguration();

	context.setJavaClientGeneratorConfiguration(javaClientGeneratorConfiguration);

	Properties attributes = parseAttributes(node);
	String type = attributes.getProperty("type"); //$NON-NLS-1$
	String targetPackage = attributes.getProperty("targetPackage"); //$NON-NLS-1$
	String targetProject = attributes.getProperty("targetProject"); //$NON-NLS-1$
	String implementationPackage = attributes
	        .getProperty("implementationPackage"); //$NON-NLS-1$
	// idong
	String superClass = attributes.getProperty("super");
	// idong
	String nameSpace = attributes.getProperty("nameSpace");

	javaClientGeneratorConfiguration.setConfigurationType(type);
	javaClientGeneratorConfiguration.setTargetPackage(targetPackage);
	javaClientGeneratorConfiguration.setTargetProject(targetProject);
	javaClientGeneratorConfiguration
	        .setImplementationPackage(implementationPackage);
	// idong
	javaClientGeneratorConfiguration.setSuperClass(superClass);
	// idong
	javaClientGeneratorConfiguration.setNameSpace(nameSpace);

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(javaClientGeneratorConfiguration, childNode);
	    }
	}
    }

    private void parseJdbcConnection(Context context, Node node) {
	JDBCConnectionConfiguration jdbcConnectionConfiguration = new JDBCConnectionConfiguration();

	context.setJdbcConnectionConfiguration(jdbcConnectionConfiguration);

	Properties attributes = parseAttributes(node);
	String driverClass = attributes.getProperty("driverClass"); //$NON-NLS-1$
	String connectionURL = attributes.getProperty("connectionURL"); //$NON-NLS-1$
	String userId = attributes.getProperty("userId"); //$NON-NLS-1$
	String password = attributes.getProperty("password"); //$NON-NLS-1$

	jdbcConnectionConfiguration.setDriverClass(driverClass);
	jdbcConnectionConfiguration.setConnectionURL(connectionURL);

	if (StringUtility.stringHasValue(userId)) {
	    jdbcConnectionConfiguration.setUserId(userId);
	}

	if (StringUtility.stringHasValue(password)) {
	    jdbcConnectionConfiguration.setPassword(password);
	}

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(jdbcConnectionConfiguration, childNode);
	    }
	}
    }

    private void parseClassPathEntry(Configuration configuration, Node node) {
	Properties attributes = parseAttributes(node);

	configuration.addClasspathEntry(attributes.getProperty("location")); //$NON-NLS-1$
    }

    private void parseProperty(PropertyHolder propertyHolder, Node node) {
	Properties attributes = parseAttributes(node);

	String name = attributes.getProperty("name"); //$NON-NLS-1$
	String value = attributes.getProperty("value"); //$NON-NLS-1$

	propertyHolder.addProperty(name, value);
    }

    private Properties parseAttributes(Node node) {
	Properties attributes = new Properties();
	NamedNodeMap nnm = node.getAttributes();
	for (int i = 0; i < nnm.getLength(); i++) {
	    Node attribute = nnm.item(i);
	    String value = parsePropertyTokens(attribute.getNodeValue());
	    attributes.put(attribute.getNodeName(), value);
	}

	return attributes;
    }

    private String parsePropertyTokens(String string) {
	final String OPEN = "${"; //$NON-NLS-1$
	final String CLOSE = "}"; //$NON-NLS-1$

	String newString = string;
	if (newString != null) {
	    int start = newString.indexOf(OPEN);
	    int end = newString.indexOf(CLOSE);

	    while (start > -1 && end > start) {
		String prepend = newString.substring(0, start);
		String append = newString.substring(end + CLOSE.length());
		String propName = newString.substring(start + OPEN.length(),
		        end);
		String propValue = properties.getProperty(propName);
		if (propValue != null) {
		    newString = prepend + propValue + append;
		}

		start = newString.indexOf(OPEN, end);
		end = newString.indexOf(CLOSE, end);
	    }
	}

	return newString;
    }

    private void parseCommentGenerator(Context context, Node node) {
	CommentGeneratorConfiguration commentGeneratorConfiguration = new CommentGeneratorConfiguration();

	context.setCommentGeneratorConfiguration(commentGeneratorConfiguration);

	Properties attributes = parseAttributes(node);
	String type = attributes.getProperty("type"); //$NON-NLS-1$

	if (StringUtility.stringHasValue(type)) {
	    commentGeneratorConfiguration.setConfigurationType(type);
	}

	NodeList nodeList = node.getChildNodes();
	for (int i = 0; i < nodeList.getLength(); i++) {
	    Node childNode = nodeList.item(i);

	    if (childNode.getNodeType() != Node.ELEMENT_NODE) {
		continue;
	    }

	    if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
		parseProperty(commentGeneratorConfiguration, childNode);
	    }
	}
    }
}
